/*************************************************************************
 * Nuvoton Electronics Corporation confidential
 *
 * Copyright (c) 2008 by Nuvoton Electronics Corporation
 * All rights reserved
 *
 * FILENAME
 *     CY3684_intloop.c
 *
 * VERSION
 *     1.0
 *
 * DESCRIPTION
 *     NUC900 USB Host interrupt loop back test program
 *
 * HISTORY
 *     2008.06.24       Created
 *
 * REMARK
 *     None
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "kapi.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#define printf  diag_printf
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wbio.h"
#include "wblib.h"
#define printf  sysprintf
#endif
#include "nuc900_reg.h"
#include "usb.h"


//#define SYSTEM_CLOCK		18432000
//#define SYSTEM_CLOCK		16670000
//#define SYSTEM_CLOCK		15000000
#define SYSTEM_CLOCK		14318000
#define UART_BAUD_RATE		115200


#ifdef ECOS
#define sysGetTicks(TIMER0)   cyg_current_time()
#endif

USB_DEV_T  *_ptCY3684Dev = NULL;

static URB_T  tUrbIntIn, tUrbIntOut;

volatile INT	_bIsPacketReceived;
volatile INT   	_nTestPacketLen = 512;
volatile UINT32	_uTestDataCount;

__align(4096) UINT8  _u8DataBufferOutMem[4096];
__align(4096) UINT8  _u8DataBufferInMem[4096];

UINT8 *_u8DataBufferOut, *_u8DataBufferIn;

#define CMP_BUFF_SIZE		(16*1024)
__align(32) UINT8  _u8CompareBuff[CMP_BUFF_SIZE];
volatile static INT _nCmpBuffHead, _nCmpBuffTail;


static VOID  int_in_irq(URB_T *urb)
{
    if (urb->status || (urb->actual_length != _nTestPacketLen)) 
    {
        printf("int_in_irq, urb error:%d, %d\n", urb->status, urb->actual_length);
        return;
    }
    else
    {
    	if (!strncmp(urb->transfer_buffer, (CHAR *)&_u8CompareBuff[_nCmpBuffHead], urb->actual_length))
    	{
			; //printf("COMPARE OK\n");
			_nCmpBuffHead = (_nCmpBuffHead + urb->actual_length) % CMP_BUFF_SIZE;
		}
    	else
    	{
    		INT  i;
    		printf("compare error! %02x %02x %02x %02x\n", ((CHAR *)urb->transfer_buffer)[0], ((CHAR *)urb->transfer_buffer)[1], ((CHAR *)urb->transfer_buffer)[2], ((CHAR *)urb->transfer_buffer)[3]);
#if 0    		
    		for (i = 0; i < urb->actual_length; i++)
    		{
    			if (((CHAR *)urb->transfer_buffer)[i] != _u8CompareBuff[_nCmpBuffHead+i])
    				printf("%d %02x %02x\n", i, ((CHAR *)urb->transfer_buffer)[i], _u8CompareBuff[_nCmpBuffHead+i]);
    		}
#endif    		
    	}
		
    }
	_bIsPacketReceived = 1;	
}


static VOID  int_out_irq(URB_T *urb)
{
    //printf("int_out_irq - %d\n", urb->actual_length);

    if (urb->status || (urb->actual_length != _nTestPacketLen))
    {
        printf("int_out_irq, urb error:%d, %d\n", urb->status, urb->actual_length);
    }
    else
    {
		urb->transfer_buffer_length = _nTestPacketLen;

		/* if packet received, generate new output pattern */
		if (_bIsPacketReceived)
		{
			memcpy((UINT8 *)((UINT32)_u8DataBufferOut), 
					(UINT8 *)(0x8000 + _nCmpBuffTail), 
					_nTestPacketLen);
			memcpy(&_u8CompareBuff[_nCmpBuffTail], _u8DataBufferOut, _nTestPacketLen);
			_nCmpBuffTail = (_nCmpBuffTail + _nTestPacketLen) % CMP_BUFF_SIZE;
			_bIsPacketReceived = 0;
			_uTestDataCount += _nTestPacketLen;
		}
		else
			printf("Lost IN packet!\n");
	}
}


static VOID *intloop_probe(USB_DEV_T *dev, UINT32 ifnum,
                           const USB_DEV_ID_T *id)
{
    INT    	pipe, maxp;

    if ((dev->descriptor.idVendor != 0x04B4) ||
        (dev->descriptor.idProduct != 0x1005))
         return NULL;
             
    printf("intloop_probe on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum);

    _ptCY3684Dev = dev;

	memset((UINT8 *)&tUrbIntIn, 0, sizeof(tUrbIntIn));
    pipe = usb_rcvintpipe(dev, 6);
    maxp = usb_maxpacket(dev, pipe, usb_pipein(pipe));
    FILL_INT_URB(&tUrbIntIn, dev, pipe, 
    			(UINT8 *)((UINT32)_u8DataBufferIn), _nTestPacketLen,
                int_in_irq, _ptCY3684Dev, 16);
    if (USB_SubmitUrb(&tUrbIntIn))
    {
    	printf("\nsubmit irq in - failed!\n");
        return (VOID *)-EIO;
    }
    
    printf("\nintloop_probe - OK!\n");
    return (VOID *)_ptCY3684Dev;
}


static VOID intloop_disconnect(USB_DEV_T *dev, VOID *ptr)
{
    USB_UnlinkUrb(&tUrbIntIn);
    USB_UnlinkUrb(&tUrbIntOut);
}


static USB_DEV_ID_T intloop_id_table[] = 
{
    USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT,   /* match_flags */
    0x04B4, 0x1005, 0, 0, 0, 0, 0,
    0,                   /* bInterfaceClass */
    0, 0, 0 
};

USB_DRIVER_T  intloop_driver = 
{
    "INTLOOP",
    intloop_probe,
    intloop_disconnect,
    {NULL,NULL},                       /* driver_list */ 
    NULL,                              /* *ioctl */
    intloop_id_table,
    NULL,                              /* suspend */
    NULL                               /* resume */
};



void  TestLoop()
{
    INT    		pipe;
    INT			nStatus;
    INT			t0, t1;

	_nCmpBuffHead = 0;
	_nCmpBuffTail = 512;

	while ((volatile)_ptCY3684Dev == NULL)
#ifdef ECOS
		Hub_CheckIrqEvent(0);;
#else
		Hub_CheckIrqEvent();;
#endif		

	memset((UINT8 *)&tUrbIntOut, 0, sizeof(tUrbIntOut));
    pipe = usb_sndintpipe(_ptCY3684Dev, 2);
	memcpy((UINT8 *)((UINT32)_u8DataBufferOut), (UINT8 *)0x8000, _nTestPacketLen);
	memcpy(&_u8CompareBuff[_nCmpBuffTail], _u8DataBufferOut, _nTestPacketLen);
	_nCmpBuffTail = (_nCmpBuffTail + _nTestPacketLen) % CMP_BUFF_SIZE;
	_bIsPacketReceived = 0;
   	FILL_INT_URB(&tUrbIntOut, _ptCY3684Dev, pipe, (UINT8 *)((UINT32)_u8DataBufferOut), 
   				_nTestPacketLen, int_out_irq, _ptCY3684Dev, 128);
   	nStatus = USB_SubmitUrb(&tUrbIntOut);
   	if (nStatus < 0)
   	{
   		printf("Submit int-out URB error, %d\n", nStatus);
   		return;
   	}
   	else
   		printf("Submit int-out URB OK\n");

	t0 = t1 = sysGetTicks(TIMER0);
	while(1)
	{
		if (sysGetTicks(TIMER0) - t1 > 100)
		{
			int  tmp;

			tmp = (sysGetTicks(TIMER0) - t0) / 100;
			printf("%02d:%02d:%02d - %d KB (%d)\n", 
					tmp / 3600, (tmp % 3600) / 60, tmp % 60,
					_uTestDataCount/1024, USB_available_memory());
			t1 = sysGetTicks(TIMER0);
		}
	}
}



int main()
{
#ifndef ECOS
    WB_UART_T 	uart;
#endif    
	WB_PLL_T 	sysClock;

	outpw(0xB0000200, (inpw(0xB0000200)|0x200)); //enable USBH clock

	sysInvalidCache();
	sysEnableCache(CACHE_WRITE_BACK);
	//sysSetCachePages((UINT32)_u8DataBufferOut, 4096, CACHE_DISABLE);
	//sysSetCachePages((UINT32)_u8DataBufferIn, 4096, CACHE_DISABLE);
	_u8DataBufferOut = (UINT8 *)((UINT32)_u8DataBufferOutMem | 0x80000000);
	_u8DataBufferIn = (UINT8 *)((UINT32)_u8DataBufferInMem | 0x80000000);

	sysClock.pll0 = PLL_200MHZ;				//PLL0 output clock
	sysClock.pll1 = PLL_133MHZ;				//PLL1 output clock
	sysClock.cpu_src = CPU_FROM_PLL0;		//Select CPU clock source
	sysClock.ahb_clk = AHB_CPUCLK_1_2;		//Select AHB clock divider
	sysClock.apb_clk = APB_AHB_1_2;			//Select APB clock divider
	sysSetPLLConfig(&sysClock);				//Call system function call

#ifndef ECOS
	/* initialize UART */
	outpw(REG_MFSEL, inpw(REG_MFSEL) | 0x100);
    uart.uiFreq = SYSTEM_CLOCK;
    uart.uiBaudrate = UART_BAUD_RATE;
    uart.uiDataBits = WB_DATA_BITS_8;
    uart.uiStopBits = WB_STOP_BITS_1;
    uart.uiParity = WB_PARITY_NONE;
    uart.uiRxTriggerLevel = LEVEL_1_BYTE;
    sysInitializeUART(&uart);
	printf("UART initialized\n");

	sysSetTimerReferenceClock (TIMER0, SYSTEM_CLOCK);
	sysStartTimer(TIMER0, 100, PERIODIC_MODE);
#endif

	InitUsbSystem();       
    USB_RegisterDriver(&intloop_driver);
	TestLoop();
	return 0;
}


#ifndef ECOS
/*
 * standalone.c - minimal bootstrap for C library
 * Copyright (C) 2000 ARM Limited.
 * All rights reserved.
 */

/*
 * RCS $Revision: 1 $
 * Checkin $Date: 08/08/22 9:59p $ 0
 * Revising $Author: Mncheng $
 */

/*
 * This code defines a run-time environment for the C library.
 * Without this, the C startup code will attempt to use semi-hosting
 * calls to get environment information.
 */
 extern unsigned int Image$$ZI$$Limit;

void _sys_exit(int return_code)
{
label:  goto label; /* endless loop */
}

void _ttywrch(int ch)
{
    char tempch = (char)ch;
    (void)tempch;
}



__value_in_regs struct R0_R3 {unsigned heap_base, stack_base, heap_limit, stack_limit;} 
    __user_initial_stackheap(unsigned int R0, unsigned int SP, unsigned int R2, unsigned int SL)
{
    struct R0_R3 config;

    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
    config.stack_base = 0x800000; //Stack base;

/*
To place heap_base directly above the ZI area, use:
    extern unsigned int Image$$ZI$$Limit;
    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
(or &Image$$region_name$$ZI$$Limit for scatterloaded images)

To specify the limits for the heap & stack, use e.g:
    config.heap_limit = SL;
    config.stack_limit = SL;
*/

    return config;
}

/* end of file standalone.c */

/* end of file standalone.c */
#endif